using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

namespace BizTalk.ESB.Extensions.Helpers
{
    [Serializable]
    public class XmlTools
    {
        public void ValidateAgainstSchema(System.Xml.XmlDocument businessDocument, String schemaStrongName)
        {
            // Constants
            const int PARTS_IN_SCHEMA_STRONG_NAME = 2;
            const int PART_CLASS_NAME = 0;
            const int PART_QUALIFIED_ASSEMBLY_NAME = 1;

            // Parse schema strong name
            string[] assemblyNameParts = schemaStrongName.Split(new char[] { ',' }, PARTS_IN_SCHEMA_STRONG_NAME);
            string className = assemblyNameParts[PART_CLASS_NAME].Trim();
            string fullyQualifiedAssemblyName = assemblyNameParts[PART_QUALIFIED_ASSEMBLY_NAME].Trim();

            // Load assembly
            System.Reflection.Assembly schemaAssembly = System.Reflection.Assembly.Load(fullyQualifiedAssemblyName);

            // Create instance of the BTS schema in order to get to the actual schemas
            Type schemaType = schemaAssembly.GetType(className);
            Microsoft.XLANGs.BaseTypes.SchemaBase btsSchemaCollection = (Microsoft.XLANGs.BaseTypes.SchemaBase)Activator.CreateInstance(schemaType);
            System.Xml.Schema.XmlSchema msgSchema = btsSchemaCollection.Schema; 

            // Set up XML validating reader and validate document
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ValidationType = System.Xml.ValidationType.Schema;

            settings.Schemas.Add(msgSchema);

            using (System.Xml.XmlReader r = XmlReader.Create(new System.IO.StringReader(businessDocument.OuterXml), settings))
            {
                while (r.Read()) { }
            }
        }

        public static System.Xml.XmlDocument AddNamespace(System.Xml.XmlDocument Message, String Namespace, Boolean Debug)
        {
            try
            {
                System.Xml.XmlDocument MessageWithNamespace = Message;
                System.Xml.XmlAttribute NamespaceAttribute = MessageWithNamespace.CreateAttribute("xmlns");
                NamespaceAttribute.Value = Namespace;
                MessageWithNamespace.DocumentElement.Attributes.Append(NamespaceAttribute);
                return MessageWithNamespace;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLineIf(Debug,
                    DebugMessageFactory.GenerateTraceMessage(
                        System.Reflection.Assembly.GetExecutingAssembly().FullName,
                        System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                        ex.Message,
                        System.Diagnostics.EventLogEntryType.Error));
                return Message;
            }
        }

        public static void SetSingleValue(System.Xml.XmlDocument Message, String Xpath, String Value, Boolean Debug)
        {
            System.Xml.XmlNode node = Message.SelectSingleNode(Xpath);
            if (node != null)
            {
                node.InnerText = Value;
            }
            else
            {
                String ErrorMessage = "Xpath returned a null node";
                System.Diagnostics.Trace.WriteLineIf(Debug,
                    DebugMessageFactory.GenerateTraceMessage(
                        System.Reflection.Assembly.GetExecutingAssembly().FullName,
                        System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                        ErrorMessage,
                        System.Diagnostics.EventLogEntryType.FailureAudit));
            }
        }

        public static String GetSingleValue(System.Xml.XmlDocument Message, String Xpath, Boolean Debug)
        {
            System.Xml.XmlNode node = Message.SelectSingleNode(Xpath);
            if (node != null)
            {
                return node.InnerText;
            }
            else
            {
                String ErrorMessage = "Xpath returned a null node";
                System.Diagnostics.Trace.WriteLineIf(Debug,
                    DebugMessageFactory.GenerateTraceMessage(
                        System.Reflection.Assembly.GetExecutingAssembly().FullName,
                        System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                        ErrorMessage,
                        System.Diagnostics.EventLogEntryType.FailureAudit));
                return String.Empty;
            }
        }

        public static String GetXmlExceptionMessage(System.Xml.XmlDocument Message, String Xpath, Boolean Debug)
        {
            XmlDocument xmlDoc = new XmlDocument();
            String retVal = String.Empty;
            System.Xml.XmlNode node = Message.SelectSingleNode(Xpath);
           
            try
            {
                if (node != null)
                {
                    xmlDoc.LoadXml(node.InnerText);
                    if(xmlDoc.FirstChild.NamespaceURI.Equals ("http://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd"))
                    {
                        retVal = BuildXmlExceptionMessage(xmlDoc.SelectSingleNode("/*[local-name()='NACK']/*[local-name()='ErrorDescription']").InnerText, node.InnerText);
                            
                    }
                    else
                    {
                        retVal = BuildXmlExceptionMessage(xmlDoc.FirstChild.InnerText, node.InnerText);
                    }
                }
                else
                {
                    String ErrorMessage = "Xpath returned a null node";
                    System.Diagnostics.Trace.WriteLineIf(Debug,
                        DebugMessageFactory.GenerateTraceMessage(
                            System.Reflection.Assembly.GetExecutingAssembly().FullName,
                            System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                            ErrorMessage,
                            System.Diagnostics.EventLogEntryType.FailureAudit));
                    retVal = "<ExceptionMessage><Description/></ExceptionMessage>";
                }
            }
            catch (System.Xml.XmlException)
            {
                retVal = BuildXmlExceptionMessage(node.InnerText, node.InnerText);
            }

            return retVal;
        }

        private static string BuildXmlExceptionMessage(string Description, string ExceptionXml)
        {
            StringBuilder xmlMessage = new StringBuilder();

            xmlMessage.Append("<ExceptionMessage>");
            xmlMessage.Append("<Description>");
            xmlMessage.Append(Description);
            xmlMessage.Append("</Description>");
            xmlMessage.Append("<Exception>");
            xmlMessage.Append("<![CDATA[");
            xmlMessage.Append(ExceptionXml);
            xmlMessage.Append("]");
            xmlMessage.Append("]>");
            xmlMessage.Append("</Exception>");
            xmlMessage.Append("</ExceptionMessage>");

            return xmlMessage.ToString();
        }

        
        public static Int32 GetNodeCount(System.Xml.XmlDocument Message, String Xpath, Boolean Debug)
        {
            if (Message.SelectNodes(Xpath) != null)
            {
                return Message.SelectNodes(Xpath).Count;
            }
            else
            {
                String ErrorMessage = "Xpath returned a null node list";
                System.Diagnostics.Trace.WriteLineIf(Debug,
                    DebugMessageFactory.GenerateTraceMessage(
                        System.Reflection.Assembly.GetExecutingAssembly().FullName,
                        System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                        ErrorMessage,
                        System.Diagnostics.EventLogEntryType.FailureAudit));
                return 0;
            }
        }

        public static System.Xml.XmlDocument SeparateChildNode(System.Xml.XmlDocument Parent, String Xpath, Int32 Index, Boolean Debug)
        {
            System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
            xmlDoc.LoadXml(Parent.Clone().OuterXml);
            if (!String.IsNullOrEmpty(Xpath))
            {
                System.Xml.XmlNodeList nodes = xmlDoc.SelectNodes(Xpath);
                if (nodes != null)
                {
                    int i = 0;
                    foreach (System.Xml.XmlNode node in nodes)
                    {
                        if (i != Index)
                        {
                            node.ParentNode.RemoveChild(node);
                        }
                        i++;
                    }
                    return xmlDoc;
                }
                else
                {
                    String ErrorMessage = "Xpath returned a null node list";
                    System.Diagnostics.Trace.WriteLineIf(Debug,
                        DebugMessageFactory.GenerateTraceMessage(
                            System.Reflection.Assembly.GetExecutingAssembly().FullName,
                            System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                            ErrorMessage,
                            System.Diagnostics.EventLogEntryType.FailureAudit));
                    return null;
                }
            }
            else
            {
                String ErrorMessage = "Xpath was null or empty";
                System.Diagnostics.Trace.WriteLineIf(Debug,
                    DebugMessageFactory.GenerateTraceMessage(
                        System.Reflection.Assembly.GetExecutingAssembly().FullName,
                        System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                        ErrorMessage,
                        System.Diagnostics.EventLogEntryType.FailureAudit));
                return null;
            }
        }

        public static XmlDocument GetXmlFromAnyNode(XmlDocument Message, String XpathOfParent, Boolean Debug)
        {
            XmlDocument xmlDoc = new XmlDocument();
            if (!String.IsNullOrEmpty(XpathOfParent))
            {
                XmlNode parentNode = Message.SelectSingleNode(XpathOfParent);
                if (parentNode != null)
                {
                    xmlDoc.LoadXml(parentNode.InnerXml);
                }
                else
                {
                    String ErrorMessage = "Xpath returned a null node";
                    System.Diagnostics.Trace.WriteLineIf(Debug,
                        DebugMessageFactory.GenerateTraceMessage(
                            System.Reflection.Assembly.GetExecutingAssembly().FullName,
                            System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                            ErrorMessage,
                            System.Diagnostics.EventLogEntryType.FailureAudit));
                }
            }
            else
            {
                String ErrorMessage = "Xpath is null or empty";
                System.Diagnostics.Trace.WriteLineIf(Debug,
                    DebugMessageFactory.GenerateTraceMessage(
                        System.Reflection.Assembly.GetExecutingAssembly().FullName,
                        System.Reflection.Assembly.GetExecutingAssembly().HostContext.ToString(),
                        ErrorMessage,
                        System.Diagnostics.EventLogEntryType.FailureAudit));
            }
            return xmlDoc;
        }
    }
}
